#include "CentralCache.hpp"
#include "PageCache.hpp"

CentralCache CentralCache::_sInst; // 定义

// 获取一个非空Span
Span *CentralCache::GetOneSpan(SpanList &list, size_t byte_size)
{
    // cout << "GetOneSpan" << endl;
    // 查看当前spanlist中是否还有未分配对象的span
    Span *it = list.Begin();
    // cout<<it<<endl;
    while (it != list.End())
    {
        if (it->_freeList != nullptr)
        {
            return it;
        }
        else
        {
            it = it->_next;
        }
    }
    // cout<<it<<endl;

    // 先把centralcache的桶锁解掉 这样如果有其他线程释放内存对象回来 不会阻塞
    list._mtx.unlock();
    // 走到这里说明没有空闲span了 只能找page cache申请
    PageCache::GetInstance()->_pageMtx.lock();
    // 加page页锁
    Span *span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(byte_size));
    // cout << "NewSpan success" <<span<< endl;
    span->_isUse = true;
    span->_objSize = byte_size;
    // cout<<span->_objSize<<endl;
    PageCache::GetInstance()->_pageMtx.unlock();
    // 对获取span进行切分，不需要加锁，因为其他线程访问不到这个span
    // 计算span的大块内存的起始地址和大块内存的大小（字节数）
    char *start = (char *)(span->_pageId << (PAGE_SHIFT));
    // cout<<"Central"<<span->_pageId<<endl;
    // cout<<(span->_pageId<<PAGE_SHIFT)<<endl;
    // cout<<"malloc"<<start<<endl;
    // 通过页号计算页的起始地址 页号<<PAGE_SHIFT 第几个页就是第几个4KB
    // size_t bytes = span->_pageId << PAGE_SHIFT;
    
    size_t bytes = span->_n << PAGE_SHIFT;
    char *end = start + bytes;

    // 把大块内存切成自由链表链接起来
    // 1.先切一块做头，方便尾插
    span->_freeList = start;
    start += byte_size;
    void *tail = span->_freeList;
    int i = 1;
    while (start < end)
    {
        ++i;
        NextObj(tail) = start;
        // cout<<i<<endl;
        tail = NextObj(tail);
        start += byte_size;
    }
    NextObj(tail) = nullptr;
    // cout<<"NextObj(tail) = nullptr;"<<endl;
    // 切好span后，需要把span挂到桶里去，加桶锁
    list._mtx.lock();
    list.PushFront(span);
    // cout<<"PushFront"<<endl;
    return span;
}
// 从中心缓存分配一定数量的对象给thread cache
size_t CentralCache::FetchRangeObj(void *&start, void *&end, size_t batchNum, size_t size)
{
    size_t index = SizeClass::Index(size);
    _spanLists[index]._mtx.lock();

    Span *span = GetOneSpan(_spanLists[index], size);
    assert(span);
    assert(span->_freeList);

    // 从span中获取batchNum个对象
    // 如果不够batchNum 则有多少拿多少
    start = span->_freeList;
    end = start;
    size_t i = 0;
    size_t actualNum = 1;
    while (i < batchNum - 1 && NextObj(end) != nullptr)
    {
        end = NextObj(end);
        ++i;
        ++actualNum;
    }
    span->_freeList = NextObj(end);
    NextObj(end) = nullptr;
    span->_useCount += actualNum;

    _spanLists[index]._mtx.unlock();
    return actualNum;
}
// 将一定数量的对象释放到span跨度
void CentralCache::ReleaseListToSpans(void *start, size_t bytes)
{
    size_t index = SizeClass::Index(bytes);
    _spanLists[index]._mtx.lock();
    while (start)
    {
        void *next = NextObj(start);
        Span *span = PageCache::GetInstance()->MapObjectToSpan(start);
        NextObj(start) = span->_freeList;
        span->_freeList = start;
        // 将对象挂到span的freelist
        span->_useCount--;
        // span的所有小块内存都回来了
        // 该span回收给pagecache pagecache做内存碎片管理
        if (span->_useCount == 0)
        {
            _spanLists[index].Erase(span);
            span->_freeList = nullptr;
            span->_next = nullptr;
            span->_prev = nullptr;
            // 将span悬空 保存

            // 释放span给pagecache
            _spanLists[index]._mtx.unlock();

            PageCache::GetInstance()->_pageMtx.lock();
            PageCache::GetInstance()->ReleaseSpanToPageCache(span);
            PageCache::GetInstance()->_pageMtx.unlock();

            _spanLists[index]._mtx.unlock();
        }
        start = next;
    }
    _spanLists[index]._mtx.unlock();
}
